#include<stdio.h>
//程序1
//指针函数
/*
	指针函数：
		我们说函数的类型，事实上指的就是函数的返回值。根据需求，一个函数可以返回字符型、整型和浮点型这些类型的数据，当然，它还可以返回指针类型的数据。定义的时候只需要跟定义指针变量一样，在类型后边加一个星号即可
		指针函数就是一个函数，在函数调用时，会返回一个指针（即某个变量的地址），可以通过返回的指针间接操作函数内部的内容
	
	格式：
		数据类型（int，void,float） *函数名（参数列表）

	特点：
		函数的返回值是一个地址（即指针）。
		调用该函数后，可以通过返回的地址操作相应的数据。


*/
char* getword(char);

char* getword(char c)
{
	switch(c)
	{
		case 'A': return "APPLE";
		case 'B': return "Banana";
		case 'C': return "Cat";
		case 'D': return "Dog";
		default: return "None";
	}
}

void test1()
{
	char n;
	printf("请输入一个字母:（A~D）");
	scanf("%c", &n);
	printf("%s",getword(n));

}

//程序2
//指向函数的指针（函数指针）
/*
	指向函数的指针:
		函数指针是一个指针变量，它指向的是一个函数的地址(函数也是一个变量)，通过它可以间接调用函数

	格式：
		返回值类型 (*指针变量名)(参数列表);

	特点：
		它存储的是函数的地址。
		调用时通过函数指针间接执行目标函数。

	函数指针和指针函数：
		指针函数 -> int *p();

		函数指针 -> int (*p)()
	
	两者区分：
		**是否有返回值为指针的函数：**如果函数返回一个指针，就是指针函数。
		**是否是存储函数地址的指针：**如果变量存储的是一个函数地址，就是函数指针


*/
int square(int);

int square(int num)
{
	return num * num;

}


void test2()
{
	int num;
	int (*fp)(int);
	/*
		int: 这是函数指针所指向的函数的返回类型。在这里，它表示所指向的函数将返回一个整数（int）类型的值。

		(*fp): 这是一个指向函数的指针的声明。fp 是这个指针的名称。括号中的星号 * 表示 fp 是一个指针，而括号本身则用于指定指针的类型是函数指针。因此，(*fp) 表示 fp 是一个指针，它指向的是一些函数。

		(int): 这是括号内的函数参数列表，表示 fp 指向的函数需要接受一个整数类型的参数。
	
	*/
	printf("请输入一个整数:");
	scanf("%d", &num);

	fp = square;

	printf("%d * %d = %d", num, num, (*fp)(num));

}


//程序3
//函数返回值运用
int add(int, int);
int sub(int, int);
int calc(int (*fp)(int, int), int, int);

int add(int num1, int num2)
{
	return num1 + num2;
}

int sub(int num1, int num2)
{
	return num1 - num2;
}

int calc(int (*fp)(int, int), int num1, int num2)
{
	return (*fp)(num1, num2);
}

void test3()
{
	int num1, num2;
	 
	printf("请输入两个数：");
	scanf("%d%d", &num1,&num2);
	printf("%d+%d = %d\n", num1, num2,calc(add, num1, num2));
	printf("%d - %d = %d", num1, num2, calc(sub, num1, num2));

}

//程序4
//一个函数能否有时候返回整型指针，有时候能返回字符指针
//请编写一个程序，要求用户输入两个数，分别计算它们加减乘除的结果。
/*
	请确保你的代码符合下面要求：
	加减乘除分别由四个函数实现：add、sub、mul 和 divi
	定义一个函数指针数组指向上面四个函数
	只能通过函数指针的形式访问上面四个函数
	需要支持浮点数，注意除数不能为 0
*/

#include<stdio.h>
#include<stdlib.h>

#define EPSINON 0.000001

double  add1(double, double);
double  sub1(double, double);
double  mul(double, double);
double  divi(double, double);

double add1(double num1, double num2)
{
	return num1 + num2;
}

double sub1(double num1, double num2)
{
	return num1 - num2;
}

double mul(double num1, double num2)
{
	return num1 * num2;
}

double divi(double num1, double num2)
{
	//不要对浮点数进行==或!=比较，因为IEEE浮点数是一个近似值
	/*
	 num2 是否可以被用作除数，即是否接近于零。如果 num2 太接近于零（在 -EPSINON 和 EPSINON 之间），程序会输出“除数不能为0”并退出，以避免除以零的情况发生。
	之所以不能用 num2 == 0 进行判断，是因为浮点数在计算机中是以近似值存储的。
	这可能会导致一些看似应该等于零的浮点数实际上存储的是一个非常小的非零值（比如由于之前的计算操作导致的舍入误差）。
	直接使用 == 或 != 来比较浮点数与零，可能会因为这种舍入误差而导致程序行为不符合预期
	因此，通常的做法是使用一个小的阈值（如 EPSINON）来检查浮点数是否“足够接近”零，而不是直接进行相等或不等的比较。这样可以提高程序的鲁棒性，减少由于浮点数精度问题导致的错误。
	*/
	if (num2 >= -EPSINON && num2 <= EPSINON)
	{
		printf("除数不能为0\n");
		// 如果除数为0，调用exit()函数直接退出程序
		exit(1);
	}

	else
	{
		return num1 / num2;
	}
}



void test4()
{
	double num1, num2,result;
	
	double (*fp[4])(double, double) = { add1,sub1,mul,divi };
	// 定义一个指向函数的指针数组，数组的元素个数为4，分别指向 add1、sub1、mul、divi 函数。

	printf("请输入两个数字：");
	scanf("%lf %lf",&num1,&num2);//这是不是1，这是英文字母 'l'
	
	
	printf("运算结果是:");
	for (int i = 0; i < 4; i++)
	{
		result = (*fp[i])(num1, num2);
		printf("%.2f ", result);
	}
	putchar('\n');
}

int main()
{
	/*
		索引				作用
		test1			指针函数说明
		test2			指向函数的指针（函数指针）
		test3			函数返回值运用
		test4			一个函数能否有时候返回整型指针，有时候能返回字符指针
	*/
	test4();
	return 0;
}